import models
import math
from collections import defaultdict
import sets
import sys

"""
This file contains the graph relaxation code for generating platform candidates

"""


def content_value():
#calculates the satisfaction the community has on this value
    pass


def sd(mean, vals, n):
#returns the standard deviation of 'n' samples held in vals, with mean=mean
    sd = 0
    if n > 0:
        for i in vals:
            sd += pow((mean - i), 2)
        sd = math.sqrt(sd / n)
    return sd


def user_preference_calc(website, user_id):
    #retrieve the mean and standard deviation of this user's voting distribution
    votes = models.get_item(website, 'vote', {'user_id': user_id})
    subjective_tot = 0.0
    subjective_num = 0
    obj = []
    objective_tot = 0.0
    objective_num = 0
    subj = []
    for vote in votes:
        if vote['params'] == ['subjective']:
            subjective_tot += int(vote['vote']["vote_type"])
            subjective_num += 1
            subj.append(int(vote['vote']["vote_type"]))
        if vote['params'] == ['objective']:
            objective_tot += int(vote['vote']["vote_pos"])
            objective_num += 1
            obj.append(int(vote['vote']["vote_pos"]))
    if subjective_num != 0:
        print 'subj_calc: ' + str(subjective_tot) + ' / ' + str(subjective_num) + ' = ' + str(subjective_tot / subjective_num)
        subj_mean = subjective_tot / subjective_num
    else:
        subj_mean = None
    if objective_num != 0:
        print 'objective_calc: ' + str(objective_tot) + ' / ' + str(objective_num) + ' = ' + str(objective_tot / objective_num)
        obj_mean = objective_tot / objective_num
    else:
        obj_mean = None
    #got mean, now get the standard deviation
    #for each val i, take user's value as (i - mean)
    #all user's average voting power goes to zero
    return {'subjective': subj_mean, 'objective': obj_mean}


def get_pair(x, y):
#returns a sorted list of the vals in a pair (x, y) where x < y
    if x < y:
        return (x, y)
    else:
        return (y, x)


def generate_candidates(website, votes, content, platforms, k):
#first we build the trust network
    g = ContentGraph(website, content, votes, platforms, k)
    ###now run iteration and trim the grah until we get k-sized subsets
    while True:
        print 'iterate'
        keep_going = g.iterate()
        g.find_subsets()
        if keep_going == False:
            break
    return g.results


class ContentGraph():
    """Initialize the Graph
        **First create G and E, populate G with utlity of each node
        **populate E with degree for each pair of connected nodes
    """

    def __init__(self, website, content, votes, platforms, k):
        self.website = website
        self.G = {}
        self.E = {}
        self.sets = []
        self.results = []
        try:
            self.k = int(k)
        except:
            raise ValueError('value k must be integer')
        self.user_preferences = defaultdict(dict)

        #get all the graphe nodes via the candidate platforms
        print platforms
        for platform in platforms['platforms']:
            for plank in platform['planks']:
                self.G[plank] = {'obj': 0, 'obj_n': 0, 'subj': 0, 'subj_n': 0}
        #store each vote
        print 'votelength: ' + str(len(list(votes)))
        for i in votes:
            for vote in i:
                if vote['params'] == ['objective']:
                    val = self.get_user_pref(website, vote)
                    #print 'user pref: ' + str(val)
                    self.G[vote['vote']['object_pk']]['obj'] += val
                    self.G[vote['vote']['object_pk']]['obj_n'] += 1
                elif vote['params'] == ['subjective']:
                    val = self.get_user_pref(website, vote)
                    self.G[vote['vote']['object_pk']]['subj'] += val
                    self.G[vote['vote']['object_pk']]['subj_n'] += 1

        print 'get utility'
        #now get utility of each content item
        #for each utility get the user_mean to give equal voting power
        for key, p in self.G.items():
            print 'votepower: ' + str(key) + ' : ' + str(p)
            self.G[key] = (p['obj'] / (p['obj_n'] + 1)) + (p['subj'] / (p['subj_n'] + 1))

        ###now calculate the edge strength with the user's candidate platforms
        for platform in platforms['platforms']:
            for x in platform['planks']:
                for y in platform['planks']:
                    if x != y:
                        pair = get_pair(x, y)
                        try:
                            self.E[pair] += .5
                        except:
                            self.E[pair] = .5
        print self.G
        print self.E
        ###Everything should be initialized and ready to iterate!

    def get_user_pref(self, website, vote):
        """returns the user's utility u_i = v_i - mean_i"""
        user_pk = vote['user_id']
        if user_pk not in self.user_preferences:
            self.user_preferences[user_pk] = user_preference_calc(website, user_pk)
            user_mean = self.user_preferences[user_pk]
        else:
            user_mean = self.user_preferences[user_pk]
        ###multiple vote type, should clean this up in the API
        try:
            t = 'objective'
            user_vote = vote['vote']['vote_pos']
        except:
            t = 'subjective'
            user_vote = vote['vote']['vote_type']
        #print 'votecalc: ' + str(user_vote) + ' - ' + str(user_mean[t]) + " + " + str(user_vote - user_mean[t])
        return user_vote

    #each iteration remove lowest edge and node
    #then search for k sized subsets
    def iterate(self):
        #get min ndoe
        mn = sys.maxint
        idx = None
        for key, val in self.G.items():
            if val < mn:
                mn = val
                idx = key
        #remove edges with this node
        for key, val in self.E.items():
            if idx in key:
                del self.E[key]
        del self.G[idx]
        if len(self.G) == 0:
            return False
        else:
            return True

    #finds subsets of k-size and stores in results
    def find_subsets(self):
        self.sets = []
        for pair, val in self.E.items():
            p = sets.Set(pair)
            add = False
            nextset = []
            for s in self.sets:
                if p.issubset(s) or pair[0] in s or pair[1] in s:
                    s = s.union(p)
                    add = True
                    nextset.append(s)
            if add == False:
                nextset.append(p)
            self.sets = nextset
        print self.sets
        for s in self.sets:
            if len(s) == self.k:
                self.results.append(s)
